#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include "mpi.h"
#include "palindromo.h"

#ifndef BUFFKB
	#define TAM_BUFF 4096
#else
	#define TAM_BUFF 1024*BUFFKB
#endif


#define ARQUIVO 	"./arq/shakespe.txt"
#define ARQ_PALI	"./resultados/shake_pal_mpi.txt"

char buffer1[TAM_BUFF];
char buffer2[TAM_BUFF];

int main(int argc, char **argv)
{
	int rank;
	int nproc;
	int pfd, pfd_resul;
	int it, lido, fimb;
	int dest, ultimo;
	int tam;
	MPI_Status Stat;
 	unsigned long t1, t2;

	int palavras, palindromos, frases, frasespal;
	int pplflag, fplflag;
	int iniciop, fimp;
	int iniciof, fimf;
	int bufval[4], buftotal[4];

	MPI_Init(&argc, &argv);
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);

	if (rank == 0)
	{			
		t1 = clock();
	
		MPI_Comm_size(MPI_COMM_WORLD, &nproc);
		nproc--;
		
		if ((pfd = open(ARQUIVO, O_RDONLY, 0 )) == -1)
		{
		    fprintf(stderr, "Nao foi possivel abrir: %s.\n", ARQUIVO );
		    exit(1);
		}
		if ((pfd_resul = open(ARQ_PALI, O_WRONLY | O_CREAT | O_TRUNC,
					S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
		{
		    fprintf(stderr, "Nao foi possivel abrir: %s.\n", ARQ_PALI );
		    exit(1);
		}

		dest = it = 0;
		do{
			lido = read(pfd, (void *) buffer1, TAM_BUFF);
			it++;
			if(lido == TAM_BUFF){
				fimb = TAM_BUFF-1;
				while(fimb < lido && buffer1[fimb] != '\n' && buffer1[fimb] != '\r'
					&& buffer1[fimb] != '.' && buffer1[fimb] != ':' && buffer1[fimb] != ';'
					&& buffer1[fimb] != '?' && buffer1[fimb] != '!') fimb++;
				fimb++;
				fimb -= TAM_BUFF;
				if(fimb < 0) lseek(pfd, SEEK_CUR, fimb);
			}


			//Receber Enviar
			MPI_Recv(&tam, 1, MPI_INT, dest+1, 1, MPI_COMM_WORLD, &Stat);
			if(tam > 0){
				MPI_Recv(buffer2, tam , MPI_CHAR, dest+1, 2, MPI_COMM_WORLD, &Stat);
				write(pfd_resul, buffer2, tam);
			}

			MPI_Send(&lido, 1, MPI_INT, dest+1, 3, MPI_COMM_WORLD);
			MPI_Send(buffer1, lido, MPI_CHAR, dest+1, 4, MPI_COMM_WORLD);

			dest++;
			dest %= nproc;
		} while(lido == TAM_BUFF);

		lido = 0;
		ultimo = dest;
		do {
			MPI_Recv(&tam, 1, MPI_INT, dest+1, 1, MPI_COMM_WORLD, &Stat);
			if(tam > 0){
				MPI_Recv(buffer2, tam , MPI_CHAR, dest+1, 2, MPI_COMM_WORLD, &Stat);
				write(pfd_resul, buffer2, tam);
			}
			MPI_Send(&lido, 1, MPI_INT, dest+1, 3, MPI_COMM_WORLD);
			dest++;
			dest %= nproc;
		} while(dest!=ultimo);

		bufval[0] = bufval[1] = bufval[2] = bufval[3] = 0;
		MPI_Reduce(bufval, buftotal, 4, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
	    palavras = buftotal[0];
		palindromos = buftotal[1];
		frases = buftotal[2];
		frasespal = buftotal[3];

		t2 = clock();		


		printf("\nResultados:\n\n");
		printf("\tTempo total: %lfs\n", ((double) (t2 - t1))/CLOCKS_PER_SEC);
		printf("\tAcessos ao arquivo: %d\n", it);
		printf("\tFrases:   %d palindromos\n", frasespal);
		printf("\t          %d total\n", frases);
		printf("\tPalavras: %d palindromos\n", palindromos);
		printf("\t          %d total\n", palavras);


		close(pfd);
		close(pfd_resul);
	}

	else {

		tam = palavras = palindromos = frases = frasespal = 0;
		MPI_Send(&tam, 1, MPI_INT, 0, 1, MPI_COMM_WORLD);
		MPI_Recv(&lido, 1, MPI_INT, 0, 3, MPI_COMM_WORLD, &Stat);

		while(lido > 0){
			MPI_Recv(buffer1, lido , MPI_CHAR, 0, 4, MPI_COMM_WORLD, &Stat);
			
			iniciof = 0;
			while(iniciof < lido &&
				(buffer1[iniciof] == '\n' || buffer1[iniciof] == '\r' || buffer1[iniciof] == '.' || 
			 	buffer1[iniciof] == ':' || buffer1[iniciof] == ';' || buffer1[iniciof] == '?' ||
			 	buffer1[iniciof] == '!')) iniciof++;

			tam = 0;
			while(iniciof < lido){
				fimf = iniciof;
				while(fimf < lido && buffer1[fimf] != '\n' && buffer1[fimf] != '\r'
					&& buffer1[fimf] != '.' && buffer1[fimf] != ':' && buffer1[fimf] != ';'
					&& buffer1[fimf] != '?' && buffer1[fimf] != '!') fimf++;

				if(fimf == TAM_BUFF) break;


				frases++;
				fplflag = check_frase( buffer1 + iniciof, fimf - iniciof);
				if(fplflag){
					frasespal++;
					buffer1[fimf] = '\0';
					tam += sprintf(buffer2 + tam, "%s\n", buffer1 + iniciof);
				}

				iniciop = iniciof;
				while(iniciop < fimf && (UCASE(buffer1[iniciop]) < 'A' ||
					 UCASE(buffer1[iniciop]) > 'Z')) iniciop++;

				while(iniciop < fimf){
					fimp = iniciop;
					while(fimp < fimf && (UCASE(buffer1[fimp]) >= 'A' &&
					  	UCASE(buffer1[fimp]) <= 'Z')) fimp++;

					palavras++;
					pplflag = check_palavra( buffer1 + iniciop, fimp - iniciop);

					if(pplflag){
						palindromos++;
						buffer1[fimp] = '\0';
						tam += sprintf(buffer2 + tam, "%s\n", buffer1 + iniciop);
					}

					iniciop = fimp;
					while(iniciop < lido && (UCASE(buffer1[iniciop]) < 'A' ||
						  UCASE(buffer1[iniciop]) > 'Z')) iniciop++;
				}

				iniciof = fimf + 1;
				while(iniciof < lido &&
					(buffer1[iniciof] == '\n' || buffer1[iniciof] == '\r' || buffer1[iniciof] == '.' || 
			 		buffer1[iniciof] == ':' || buffer1[iniciof] == ';' || buffer1[iniciof] == '?' ||
			 		buffer1[iniciof] == '!')) iniciof++;
			}

			MPI_Send(&tam, 1, MPI_INT, 0, 1, MPI_COMM_WORLD);
			if(tam > 0){
				MPI_Send(buffer2, tam, MPI_CHAR, 0, 2, MPI_COMM_WORLD);
			}
			MPI_Recv(&lido, 1, MPI_INT, 0, 3, MPI_COMM_WORLD, &Stat);
		}

		bufval[0] = palavras;
		bufval[1] = palindromos;
		bufval[2] = frases;
		bufval[3] = frasespal;
		MPI_Reduce(bufval, buftotal, 4, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
	}

	MPI_Finalize();
	return 0;
}
